﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
using Microsoft.Win32;
using Prism.Common.Interfaces;
using Microsoft.Practices.Prism.Commands;
using Mitosoft.Definitions.Data;
using Mitosoft.Definitions.ViewModels;
using Mitosoft.TextFileReader;

namespace ChannelNamesParsingModule.ViewModels
{
	/// <summary>
	/// View model used for the module being responsible to evaluate log file contents against 
	/// a list of allowed log file channels.
	/// </summary>
	// ReSharper disable ClassNeverInstantiated.Global
	public class MainViewModel : BaseViewModel
	// ReSharper restore ClassNeverInstantiated.Global
	{
		#region fields

		/// <summary>
		/// Represents <see cref="SelectChannelNamesFileCommand"/>
		/// </summary>
		protected ICommand _selectChannelNamesFileCommand;

		/// <summary>
		/// Represents <see cref="EvaluateCommand"/>
		/// </summary>
		protected ICommand _evaluateCommand;

		/// <summary>
		/// Represents <see cref="ChannelNamesFile"/>
		/// </summary>
		protected string _channelNamesFile;

		/// <summary>
		/// Size of cache used to store line contents.
		/// </summary>
		protected const uint LINE_CONTENT_CACHE_SIZE = 1024;

		/// <summary>
		/// Size of cache used to store line positions.
		/// </summary>
		protected const uint LINE_POSITIONS_CACHE_SIZE = 1024;

		/// <summary>
		/// List containing the allowed log channel names.
		/// </summary>
		protected readonly List<LineContent> _channelNames = new List<LineContent>();

		/// <summary>
		/// Represents the service being used to evaluate a log file agains given log channels.
		/// </summary>
		protected readonly IEvaluateLogChannelsService _evaluateLogChannelsService;

		/// <summary>
		/// Represents <see cref="EvaluatingResultsText"/>
		/// </summary>
		protected string _evaluatingResultsText;

		/// <summary>
		/// Represents <see cref="EnableDisplayingLogLineNumbersOfUndefinedChannels"/>
		/// </summary>
		protected bool _enableDisplayingLogLineNumbersOfUndefinedChannels;

		/// <summary>
		/// Represents <see cref="RememberedEvaluationResults"/>
		/// </summary>
		protected Dictionary<string, List<ulong>> _rememberedEvaluationResults;

		/// <summary>
		/// Represents <see cref="RememberEvaluationResults"/>
		/// </summary>
		protected bool _rememberEvaluationResults;

		protected bool _runFresh;

		#endregion

		#region constructor(s)

		/// <summary>
		/// Creates a new <see cref="MainViewModel"/> instance.
		/// </summary>
		/// <param name="evaluateLogChannelsService">Service used to evaluate the log channels.</param>
		public MainViewModel(IEvaluateLogChannelsService evaluateLogChannelsService)
		{
			_evaluateLogChannelsService = evaluateLogChannelsService;
		}

		#endregion

		#region properties

		/// <summary>
		/// Gets or sets whether the previous results should be resetted.
		/// </summary>
		public bool RunFresh
		{
			get
			{
				return _runFresh;
			}
			set
			{
				_runFresh = value;
        HandlePropertyChanged();
			}
		}

		/// <summary>
		/// Gets or sets whether the last evaluation results should be remembered.
		/// </summary>
		public bool RememberEvaluationResults
		{
			get
			{
				return _rememberEvaluationResults;
			}
			set
			{
				_rememberEvaluationResults = value;
        HandlePropertyChanged();
			}
		}

		/// <summary>
		/// Gets or sets the remembered evaluation results.
		/// </summary>
		protected Dictionary<string, List<ulong>> RememberedEvaluationResults
		{
			get
			{
				return 
					_rememberedEvaluationResults ?? 
						(_rememberedEvaluationResults = 
							new Dictionary<string, List<ulong>>());
			}
			set
			{
				_rememberedEvaluationResults = value;
			}
		}

		/// <summary>
		/// Determines whether the log lines of the undefined channel names should be displayed.
		/// </summary>
		public bool EnableDisplayingLogLineNumbersOfUndefinedChannels
		{
			get
			{
				return _enableDisplayingLogLineNumbersOfUndefinedChannels;
			}
			set
			{
				_enableDisplayingLogLineNumbersOfUndefinedChannels = value;

        HandlePropertyChanged();
			}
		}

		/// <summary>
		/// Text used to display the evaluation results.
		/// </summary>
		public string EvaluatingResultsText
		{
			get
			{
				return _evaluatingResultsText ?? (_evaluatingResultsText = string.Empty);
			}
			set
			{
				_evaluatingResultsText = value;
        HandlePropertyChanged();
			}
		}

		/// <summary>
		/// Command used to select the file containing the allowed log channels.
		/// </summary>
		// ReSharper disable UnusedMember.Global
		public ICommand SelectChannelNamesFileCommand
		// ReSharper restore UnusedMember.Global
		{
			get
			{
				return
					_selectChannelNamesFileCommand ??
						(_selectChannelNamesFileCommand = 
							new DelegateCommand<object>( 
								OnSelectChannelNamesFileCommandExecuted ));
			}
		}

		/// <summary>
		/// Command used to start evaluating the channel names against log file contents.
		/// </summary>
		public ICommand EvaluateCommand
		{
			get
			{
				return
					_evaluateCommand ??
						(_evaluateCommand =
							new DelegateCommand<object>(
								OnEvaluateCommandExecuted ));
			}
		}

		/// <summary>
		/// Path and name of the file containing the allowed log channels which should be used in log files.
		/// </summary>
		public string ChannelNamesFile
		{
			get
			{
				return _channelNamesFile;
			}
			set
			{
				_channelNamesFile = value;

				ICacheableDataBlockProvider<IGenericDataBlockItem<string>, string> textfileReader =
					new TextFileReader(
						value,
						LINE_POSITIONS_CACHE_SIZE,
						LINE_CONTENT_CACHE_SIZE,
            0);

				_channelNames.Clear();
				foreach( IGenericDataBlockItem<string> genericDataBlockItem in textfileReader.Values( 0, textfileReader.TotalNumberOfRecords ) )
				{
					_channelNames.Add( new LineContent( genericDataBlockItem.Value, genericDataBlockItem.Id ) );
				}

        HandlePropertyChanged();
			}
		}

		#endregion

		#region methods

		/// <summary>
		/// <see cref="SelectChannelNamesFileCommand"/> executing requested.
		/// </summary>
		/// <param name="obj">Not used.</param>
		protected void OnSelectChannelNamesFileCommandExecuted( object obj )
		{
			var dialog =
				new OpenFileDialog
				{
					DefaultExt = "*.txt",
					Filter = "files containing channel names (*.txt)|*.txt|All files (*.*)|*.*"
				};

			bool? result = dialog.ShowDialog();

			if( (bool)!result )
				return;

			ChannelNamesFile = dialog.FileName;
		}

		/// <summary>
		/// <see cref="EvaluateCommand"/> executing requested.
		/// </summary>
		/// <param name="obj">Not used.</param>
		protected void OnEvaluateCommandExecuted( object obj )
		{
			if (_evaluateLogChannelsService == null)
				return;	// no service available?

			if (RunFresh)
				_rememberedEvaluationResults = new Dictionary<string, List<ulong>>();

			_evaluateLogChannelsService.ChannelNamesFile = ChannelNamesFile;
			_evaluateLogChannelsService.Start(EvaluatingFinished);
		}

		/// <summary>
		/// Will be called when evaluating the log file is finished.
		/// </summary>
		/// <param name="results">Represents the result list.</param>
		protected void EvaluatingFinished(Dictionary<string, List<ulong>> results)
		{
		  string totalResult = RememberEvaluationResults ? EvaluatingResultsText : String.Empty;

		  if (results!= null)
			foreach (KeyValuePair<string, List<ulong>> keyValuePair in results)
			{
			  if (RememberEvaluationResults)
			  {
				  if (RememberedEvaluationResults.ContainsKey(keyValuePair.Key))
					  continue;		// Result was already found, so ignore it now.

				  RememberedEvaluationResults.Add(keyValuePair.Key, keyValuePair.Value);
			  }
				
			  totalResult += keyValuePair.Key + Environment.NewLine;

			  if (!EnableDisplayingLogLineNumbersOfUndefinedChannels || keyValuePair.Value == null || !keyValuePair.Value.Any()) 
				continue; // No line numbers available.
          
			  string foundInLineNumbers = "Found in line numbers:";

			  foreach (ulong lineNumber in keyValuePair.Value)
			  {
				foundInLineNumbers += string.Format("{0} -", lineNumber);
			  }

			  totalResult += foundInLineNumbers + Environment.NewLine;
			}

		  EvaluatingResultsText = totalResult;
		}

		#endregion
	}
}
